package com.ecld.grpc.domain;

import com.ecld.grpc.dao.AuthDao;
import com.ecld.grpc.grpc.AuthResponse;
import com.ecld.grpc.grpc.AuthTokenResponse;
import com.ecld.grpc.grpc.Token;
import com.ecld.grpc.server.GRpcServer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class AuthServiceHandler {

    public static final String PASSWD_TOO_SHORT_INFO = "Password lengh must be over 6 numbers including letters!!";
    public static final String PASSWD_CHECK_OK_INFO = "Password acceptable.";
    public static final String LOGIN_SUCCESS_INFO = "Login in success.";
    public static final String PASSWD_WRONG_INFO = "Username or password is wrong!!";
    public static final String USER_NOT_EXIST_INFO = "User not exist!!";
    public static final String LOGIN_UNSUCCESS_WITH_UNKNOWN_ERROR_INFO = "Login unsuccess with unknown error, retry or report to admin!";
    public static final String UPDATE_SUCCESS_INFO = "Change password success.";
    public static final String UPDATE_UNSUCCESS_WITH_UNKNOWN_ERROR_INFO = "Change password unsuccess with unknown error, retry or report to admin!";
    public static final String GET_TOKEN_SUCCESS_INFO = "Get token success.";
    public static final String GET_TOKEN_UNSUCCESS_WITH_UNKNOWN_ERROR_INFO = "Get token unsuccess with unknown error, retry or report to admin!";
    public static final String AUTH_SUCCESS_INFO = "Token auth success.";
    public static final String AUTH_TOKEN_UNSUCCESS_WITH_UNKNOWN_ERROR_INFO = "Token auth unsuccess with unknown error, retry or report to admin!";
    public static final String TOKEN_EXPIRED_INFO = "Token is expired!!";
    public static final String TOKEN_WRONG_INFO = "Token is wrong!!";

    private static final Logger logger = Logger.getLogger(GRpcServer.class.getName());
    private AuthDao dao = new AuthDao();
    private long expired_time = 3600000L; //one hour
    private String code = "";

    //User Login
    public String doLogin(String uname, String passwd) {

        String msg = checkPasswd(passwd);
        if(msg.equals(AuthServiceHandler.PASSWD_CHECK_OK_INFO)) {
            try {
                msg = getMD5(passwd).equals(dao.getUserPasswdDao(uname))? AuthServiceHandler.LOGIN_SUCCESS_INFO : AuthServiceHandler.PASSWD_WRONG_INFO;
            } catch (SQLException e) {
                if(e.getMessage().contains("ResultSet closed")){
                    msg = AuthServiceHandler.USER_NOT_EXIST_INFO;
                } else {
                    msg = AuthServiceHandler.LOGIN_UNSUCCESS_WITH_UNKNOWN_ERROR_INFO;
                    logger.log(Level.WARNING,"Login in Unsuccess: "+e.getMessage());
                }
            }
        }
        logger.log(Level.INFO,"User "+uname+" login in return: "+msg);
        return msg;
    }

    //Change Password
    public String doUpdateUser(String uname,String newPasswd) {

        String msg = checkPasswd(newPasswd);
        if(msg.equals(AuthServiceHandler.PASSWD_CHECK_OK_INFO)){
            try {
                dao.updateUserDao(uname,getMD5(newPasswd));
                msg = AuthServiceHandler.UPDATE_SUCCESS_INFO ;
            } catch (SQLException e) {
                msg = AuthServiceHandler.UPDATE_UNSUCCESS_WITH_UNKNOWN_ERROR_INFO;
                logger.log(Level.WARNING,"Change Password Unsuccess: "+e.getMessage());
            }
        }
        logger.log(Level.INFO,"User "+uname+" change password return: "+msg);
        return msg;
    }

    //Token get here
    public AuthResponse doTouchToken(String uname) {

        AuthResponse response;
        String msg = AuthServiceHandler.GET_TOKEN_SUCCESS_INFO;
        try {
            String tokenstr;
            long valid_until = dao.getTokenUntilDao(uname);
            //if token expired
            if(valid_until<System.currentTimeMillis()){
                //generate new token
                tokenstr = getMD5(uname+System.currentTimeMillis());
                valid_until = System.currentTimeMillis()+expired_time;
                dao.setTokenDao(uname,valid_until,tokenstr);
            } else {
                tokenstr = dao.getTokenDao(uname);
            }
            Token token = Token.newBuilder().setUser(uname).setToken(tokenstr).build();
            response = AuthResponse.newBuilder()
                    .setToken(token)
                    .setErr("")
                    .setValidUntil(valid_until).build();
        } catch (SQLException e) {
            msg = AuthServiceHandler.GET_TOKEN_UNSUCCESS_WITH_UNKNOWN_ERROR_INFO;
            response = AuthResponse.newBuilder().setErr(msg).build();
            logger.log(Level.WARNING,"Touch Token Unsuccess: "+e.getMessage());
        }
        logger.log(Level.INFO,"User "+uname+" get token return: "+msg);
        return response;
    }

    //Auth by token
    public AuthTokenResponse doAuth(Token token) {

        AuthTokenResponse response;
        String msg = AuthServiceHandler.AUTH_SUCCESS_INFO;
        try {
            boolean valid;
            long valid_until = 0;
            //if token correct
            if(dao.getTokenDao(token.getUser()).equals(token.getToken())){
                //if token expired
                valid_until = dao.getTokenUntilDao(token.getUser());
                if( valid_until >System.currentTimeMillis()){
                    valid = true;
                } else {
                    valid = false;
                    msg = AuthServiceHandler.TOKEN_EXPIRED_INFO;
                }
            } else {
                valid = false;
                msg = AuthServiceHandler.TOKEN_WRONG_INFO;
            }
            logger.log(Level.INFO,"Auth token return: "+msg);
            if(msg.equals(AuthServiceHandler.AUTH_SUCCESS_INFO)) msg = "";
            response = AuthTokenResponse.newBuilder().setValid(valid).setErr(msg).setValidUntil(valid_until).build();

        } catch (SQLException e) {
            msg = AuthServiceHandler.AUTH_TOKEN_UNSUCCESS_WITH_UNKNOWN_ERROR_INFO;
            response = AuthTokenResponse.newBuilder().setValid(false).setErr(msg).build();
            logger.log(Level.WARNING,"Auth Token Unsuccess: "+e.getMessage());
        }

        return response;
    }

    private String checkPasswd(String passwd) {
        return passwd.length()<6 ? AuthServiceHandler.PASSWD_TOO_SHORT_INFO : AuthServiceHandler.PASSWD_CHECK_OK_INFO;
    }

    private String getMD5(String message){
        message = message+code;
        String md5str = "";
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] input = message.getBytes();
            byte[] buff = md.digest(input);
            md5str = bytesToHex(buff);
        } catch (NoSuchAlgorithmException e) {
            logger.log(Level.WARNING,e.getMessage());
            return null;
        }
        return md5str;
    }

    private String bytesToHex(byte[] bytes) {
        StringBuffer md5str = new StringBuffer();
        int digital;
        for (int i = 0; i < bytes.length; i++) {
            digital = bytes[i];
            if (digital < 0) {
                digital += 256;
            }
            if (digital < 16) {
                md5str.append("0");
            }
            md5str.append(Integer.toHexString(digital));
        }
        return md5str.toString().toUpperCase();
    }


}
